package ua.org.gtug.android;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;

public abstract class Authenticator {
	private static final String ACCOUNT_TYPE_GOOGLE = "com.google";
	private static final String AUTH_TOKEN_TYPE = "oauth2:https://www.googleapis.com/auth/plus.me";

	private final Activity context;
	private final SharedPreferences prefs;
	private final int chooseAccountDialogId;
	private final int authenticationRequestId;

	private String accessToken;

	public Authenticator(final Activity context, final SharedPreferences prefs, final int chooseAccountDialogId, final int authenticationRequestId) {
		this.context = context;
		this.prefs = prefs;
		this.chooseAccountDialogId = chooseAccountDialogId;
		this.authenticationRequestId = authenticationRequestId;
	}

	public void authenticate() {
		final String accountName = prefs.getString("accountName", null);

		if (accountName != null) {
			final AccountManager manager = AccountManager.get(context);
			final Account[] accounts = manager.getAccountsByType(ACCOUNT_TYPE_GOOGLE);
			final int size = accounts.length;
			for (int i = 0; i < size; i++) {
				final Account account = accounts[i];

				if (accountName.equals(account.name)) {
					authenticate(manager, account);
					return;
				}
			}
		}

		context.showDialog(chooseAccountDialogId);
	}

	private void authenticate(final AccountManager manager, final Account account) {
		prefs.edit().putString("accountName", account.name).commit();

		new AsyncTask<Void, Void, Bundle>() {
			private Exception exception;

			@Override
			protected Bundle doInBackground(final Void... params) {
				try {
					return manager.getAuthToken(account, AUTH_TOKEN_TYPE, true, null, null).getResult();
				} catch (final Exception e) {
					exception = e;
					return null;
				}
			}

			@Override
			protected void onPostExecute(final Bundle bundle) {
				if (exception != null) {
					handleException(exception);
					return;
				}

				if (bundle.containsKey(AccountManager.KEY_INTENT)) {
					final Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT);
					intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK);
					context.startActivityForResult(intent, authenticationRequestId);
				} else if (bundle.containsKey(AccountManager.KEY_AUTHTOKEN)) {
					accessToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
					onAuthenticated(accessToken);
				}
			}
		}.execute();
	}

	public void onActivityResult(final int resultCode) {
		if (resultCode == Activity.RESULT_OK) {
			authenticate();
		} else {
			final AccountManager manager = AccountManager.get(context);
			final Account[] accounts = manager.getAccountsByType(ACCOUNT_TYPE_GOOGLE);
			final int size = accounts.length;
			final String[] names = new String[size];
			for (int i = 0; i < size; i++) {
				names[i] = accounts[i].name;
			}

			final Bundle args = new Bundle();
			args.putStringArray("accounts", names);
			context.showDialog(chooseAccountDialogId, args);
		}
	}

	public Dialog onCreateDialog() {
		final AccountManager manager = AccountManager.get(context);
		final Account[] accounts = manager.getAccountsByType(ACCOUNT_TYPE_GOOGLE);
		final int size = accounts.length;
		final String[] names = new String[size];
		for (int i = 0; i < size; i++) {
			names[i] = accounts[i].name;
		}

		return new AlertDialog.Builder(context)
				.setTitle("Select a Google account")
				.setItems(names, new DialogInterface.OnClickListener() {
					@Override
					public void onClick(final DialogInterface dialog, final int which) {
						authenticate(manager, accounts[which]);
					}
				})
				.create();
	}

	private void handleException(final Exception e) {
		Log.e("auth", "Authentication error", e);
	}

	public boolean isAuthenticated() {
		return accessToken != null;
	}

	public void invalidate() {
		AccountManager.get(context).invalidateAuthToken(ACCOUNT_TYPE_GOOGLE, accessToken);
		accessToken = null;
	}

	public abstract void onAuthenticated(String accessToken);
}
